/**
 * 所有以下代码都依赖于 jQuery
 * 
 * @author Nio Xiao
 */
 
/**
 * 解析出字符串中的 <script> .... </script> 代码。
 * 
 * @param string s
 * @return array 返回的第一个元素为非JS代码，第二个元素为JS代码片段组成的数组。
 */
var $parseoutjs = function(s){
    var re = /(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)/img;
    var scripts = s.match(re);
    s = s.replace(re,'');
    re = /(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)/im;
    if (scripts) for (var i=0; i<scripts.length; i++) scripts[i] = scripts[i].match(re)[1];
    return [s, scripts];
}
/**
 * 执行 JavaScript 代码。
 * 
 * @param array scripts
 * @return void
 */ 
var $eval = function(scripts){
    if (scripts) for (var i=0; i<scripts.length; i++) {
        var script = scripts[i];
        if (window.execScript) window.execScript( script );
        else window.setTimeout( script, 0 )
    }
}

// jQuery.param() 的别名
var $string = function(a) {
    if (a) {
        return (typeof a == 'string') ? a : $.param(a);    
    } else {
        return '';
    }
     
}

jQuery.fn.center = function () {
    this.css("position","absolute");
    this.css("top", Math.max(0, (($(window).height() - $(this).outerHeight()) / 2) + 
                                                $(window).scrollTop()) + "px");
    this.css("left", Math.max(0, (($(window).width() - $(this).outerWidth()) / 2) + 
                                                $(window).scrollLeft()) + "px");
    return this;
}

/**
 * iAjax 将 jQuery 的 Ajax 对象放入队列中，依次调用执行，
 * 同时将获取到的 JSON 对象解析成指令进行执行。此外，还设置了默
 * 认的事件触发回调函数：onRequest、onComplete 和 onFailure，
 * 在加载的时候自动显示加载信息，请求成功后指向指令，如有错误则显
 * 示错误信息。
 *
 * 应用示例：
 *     iAjax.add('/ajaxtest.php', {message: 'Hello!'});
 *
 * @author Nio Xiao
 */
var iAjax = {
    id_status: 'iajax_status', //用于显示 AJAX 状态的 DOM 元素 id
    id_overlay: 'iajax_overlay',
    id_overlay_status: 'iajax_overlay_status',
    queue: [], //AJAX 队列
    errors: [], //错误数组
    debug: false, //是否在 Firefox 的 Firebug 的控制终端中显示错误信息

    add: function(url, params, options){
        var self = this;
        // AJAX 默认选项
        var defaultOptions = {
            async: true,
            type: 'POST',
            dataType: 'json',
            // 自定义参数设置
            loadingText: '正在加载，请稍候....',
            overlay: false,
            
            beforeSend: function(xhr, settings) {
                if (settings.loadingText == '') return true; //加载文字为空，则不显示加载状态
                if (settings.overlay) {
                    self.showOverlayStatus(settings.loadingText);
                } else if (settings.loadingText) {
                    self.showInlineStatus(settings.loadingText);                
                }
                return true;
            },
            
            success : function(data, status) {
                if (this.loadingText) {
                    self.hideAllStatus();
                }
                self.handle(data);
            },
            
            error: function(xhr, status, e) {
                if (this.loadingText) {
                    self.hideAllStatus();
                }
                if (status == 'parsererror') self.errors.push({message: "从服务器接收到的 JSON 对象格式不正确。", object: xhr.responseText});
                else self.errors.push({message: 'AJAX 调用失败，服务器未响应，请确定网络连接是否正常。', exception: e});
            },
            
            complete: function(xhr, status) {
                self.doFinal();
            }
        };
        // 覆盖默认的 AJAX 选项
        var ajax = $.extend({}, defaultOptions, {url: url, data: params}, options || {});
        // 放入队列
        this.queue.push(ajax);
        if (this.queue.length == 1) {
            // 如果当前只有一个 AJAX 请求，则开始运行，多于 1 个则在队列中等待
            this.request();
        }
    },

    submitForm: function(form){
        var params = $(form).serialize();
        this.add($(form).attr('action'), params, { loadingText: '正在提交，请稍候....', overlay: true });
    },
    
    request: function(){
        this.errors = [];
        if (this.queue.length <= 0) return;
        var ajax = this.queue[0];
        this.queue.shift(); //移除第一个，因为马上就要执行过了 :)
        //var ajax = new Ajax(item.url, item.options).request(item.params);
        $.ajax(ajax);
    },
    
    requestNext: function(){
        if (this.queue.length > 0) this.request(); //执行下一个
    },
    
    showInlineStatus: function(text){
        var el_status = $('#'+this.id_status);
        if (!el_status.length) {
            el_status = $('<div id="' + this.id_status + '"></div>');
            el_status.appendTo(document.body);
        }
        el_status.html('<span>'+text+'</span>').show();    
    },
    
    hideInlineStatus: function(){
        $('#'+this.id_status).hide();
    },
    
    showOverlayStatus: function(text, cls){
        var cls = cls || 'loading',
            el_overlay = $('#'+this.id_overlay), 
            el_status = $('#'+this.id_overlay_status);
        if (!el_overlay.length) {
            el_overlay = $('<div id="'+this.id_overlay+'" class="ui-widget-overlay ui-front"></div>')
            el_overlay.appendTo(document.body);
        }
        el_overlay.show();
        if (!el_status.length) {
            el_status = $('<div id="' + this.id_overlay_status + '" class="ui-corner-all"></div>');
            el_status.appendTo(document.body);
        }
        el_status.html('<span class="'+cls+'">'+text+'</span>').center().show();
    },
    
    hideOverlayStatus: function(){
        $('#'+this.id_overlay).hide();
        $('#'+this.id_overlay_status).hide();    
    },
    
    hideAllStatus: function(){
        this.hideInlineStatus();
        this.hideOverlayStatus();
    },
    
    doFinal: function(){
        if (this.debug) {
            for (var i = 0; i < this.errors.length; i++) { //如果 AJAX 调用有错，则显示错误
                var error = this.errors[i];
                var msg = error.message;
                if (msg == undefined) continue; //用于修复页面中有 iframe 的情况
                var obj = error.object || null;
                var ex  = error.exception || null;
                if (window.ie) { //IE 则直接弹出错误对话框
                    alert(msg + "\n" + $string(obj) + "\n" + $string(ex));
                } else { //在 Firefox 的 Firebug 中显示错误
                    if (typeof console != "undefined" && typeof console.group != "undefined") {
                        console.error(msg);
                        if (obj || ex) {
                            console.group('关联异常/对象/值');
                            if (obj) console.log(obj);
                            if (ex) console.log(ex);
                            console.groupEnd();
                        }
                    } else if (typeof console != "undefined") {
                        console.error(msg + "\n" + $string(obj) + "\n" + $string(ex));
                    } else {
                        alert(msg + "\n" + $string(obj) + "\n" + $string(ex));
                    }
                }
            }
        }
        this.requestNext(); //下一个    
    },
    
    handle: function(data){
        if (data['actions'] != undefined) {
            this.process(data['actions']);
        } else {
            this.errors.push({message: "从服务器接收到的 JSON 对象格式不正确。", object: data});
        }
    },
    
    process: function(actions){        
        var action = actions.shift();
        if (!action) return;
    
        if (action.type == undefined) {
            this.errors.push({message: 'Action 必须包含 type 属性。', object: action});
        } else if (iAction[action.type] == undefined) {
            this.errors.push({message: '目前不支持动作 “' + action.type + '”。', object: action});
            this.process(actions);
            return;        
        } else {
            try {
                iAction[action.type](action);
            } catch (e) {
                this.errors.push({message: '解析执行 action “' + action.type + '” 失败。', object: action, exception: e});
            }
        }

        // 执行下一个动作
        this.process(actions);
    }
}

/**
 * iAction 为客户端动作对象，将常用的 JavaScript 代码封装成 action。
 * 可通过传递 JSON 格式的动作参数给相应方法，执行动作。
 * 如：iAction.forward({url: 'http://www.phpjobs.cn'});
 *
 * @author Nio Xiao
 */
var iAction = {
    id_notice: 'iajax_notice', //存放提示信息的 DOM 元素 id
    id_modal: 'modal_window', //Modal 窗口的 DOM id
    id_modal_cover: 'modal_cover', //Modal 窗口的背景屏蔽 div 的 DOM id
    modal_type: 'bootstrap', //Modal 窗口类型，可以是 bootstrap 或 jquery-ui
    modal_width: 530, //Modal 窗口默认宽度，0 则不设置宽度

    /**
     * 弹出对话框。
     * action.content：对话框中的内容。
     */
    alert: function(action){
        var content = action.content;
        alert(content);    
    },
    
    /**
     * 刷新当前页面。
     */
    refresh: function(action){
        window.location.reload();
    },
    
    /**
     * 跳转到其它页面。
     * action.url：跳转的目标页面 URL。
     */
    forward: function(action){
        window.location.href = action.url || null;
    },
    
    /**
     * 执行 JavaScript 代码。
     * action.js：想要执行的 JavaScript 代码。
     */
    js: function(action){
        eval(action.js || null);
    },
    
    /**
     * 更新 DOM 中某个元素的 HTML 内容。
     * action.id     ：需要更新的元素 id；
     * action.html   ：想要放入更新元素中的新的 HTML 内容，可包含 JavaScript；
     * action.animate：是否使用动态效果。
     */
    update: function(action){
        var obj  = $('#'+action.id);
        var tmp  = $parseoutjs(action.html);
        var html = tmp[0];
        var js   = tmp[1];
        if (!obj.length) {
            try {console.log('未找到 action 中 id 所指的 DOM 元素，请确定 id 是否正确。');} catch(e) { }
            return;
        }
        if (action.animate) { //是否使用动态效果
            obj.hide();
            obj.html(html);
            obj.fadeIn('slow');
        } else {
            obj.html(html);
        }
        $eval(js); //执行 HTML 里边包含的 JavaScript 代码
    },
    
    /**
     * 替换 DOM 中某个元素的 HTML 内容。
     * action.id     ：需要替换的元素 id；
     * action.html   ：用于替换的 HTML 内容，可包含 JavaScript；
     * action.animate：是否使用动态效果。
     */
    replace: function(action){
        var tmp  = $parseoutjs(action.html);
        var html = tmp[0];
        var js   = tmp[1];
        var el_old = $('#'+action.id);
        
        if (!el_old.length) {
            try {console.log('未找到 action 中 id 所指的 DOM 元素，请确定 id 是否正确。');} catch(e) { }
            return;
        }
        
        // 如果只有一个元素，则使用此元素作为最顶层，否则外层封装一个 div
        var el_new = $(html);
        el_old[0].id = null;  //we do this so safari maps id to el_new
        
        if (action.animate) { //是否使用动态效果
            el_new.hide();
            el_old.replaceWith(el_new);
            el_new.fadeIn('slow');
        } else {
            el_old.replaceWith(el_new);
        }        
        
        $eval(js); //执行 HTML 里边包含的 JavaScript 代码
    },
    
    /**
     * 往 DOM 中某个元素中增加 HTML 内容。
     * action.id     ：需要增加内容的元素 id；
     * action.html   ：用于增加的 HTML 内容，可包含 JavaScript；
     */
    append: function(action){
        var obj  = $('#'+action.id);
        var tmp  = $parseoutjs(action.html);
        var html = tmp[0];
        var js   = tmp[1];
        if (!obj.length) {
            try {console.log('未找到 action 中 id 所指的 DOM 元素，请确定 id 是否正确。');} catch(e) { }
            return;
        }
        obj.append(html);
        $eval(js); //执行 HTML 里边包含的 JavaScript 代码    
    },
        
    /**
     * 显示提示信息，并在显示之后自动隐去消失。
     * action.icon：提示图标。
     * action.content：提示信息的内容。
     */
    notice: function(action){
        var el = $('#'+this.id_notice);
        var icon = action.icon || 'alert';
        
        if (!el.length) {
            el = $('<div id="'+this.id_notice+'"></div>');
            el.appendTo(document.body);
        }
        el.hide().html('<span class="notice_'+icon+'">'+action.content+'</span>').slideDown('slow', function(){
            setTimeout(function(){ el.slideUp('slow'); }, 5000);
        });
    },        
    
    /**
     * 显示提示信息，并在显示之后自动隐去消失。
     * action.icon：提示图标。
     * action.content：提示信息的内容。
     * action.timeout：提示信息显示的时长。
     */
    showModalNotice: function(action){
        var icon = action.icon || 'ok', timeout = action.timeout || 2000;
        iAjax.showOverlayStatus(action.content, 'notice_'+icon);
        setTimeout(function(){ iAjax.hideOverlayStatus(); }, timeout);
    },    
    
    /**
     * 显示 modal 窗口。
     * action.title：窗口的标题；
     * action.width：窗口的宽度；
     * action.html ：窗口中显示的内容。
     * action.movable：窗口是否可移动。
     *
     * @param {{title: String, width: Number, html: XML|String, movable: Boolean}} action 动作设置。
     */
    showModal: function(action){    
        this.closeModal(); //首先关闭已有窗口（如果存在的话）
        
        var self = this;
        var tmp  = $parseoutjs(action.html); 
        var html = tmp[0];
        var js   = tmp[1];
        // 有文件上传的form
        if (html.match(/type="file"/)) {
            if (!$('#upload_iframe').length) $('<iframe id="upload_iframe" name="upload_iframe" src="about:blank" frameborder="0" style="width: 1px; height: 1px;" scroll="no" style="display:none"></iframe>').appendTo('body');
            html = html.replace(/(<form .* )(onsubmit="[^"]*")( [^>]*>)/mi, '$1target="upload_iframe"$3<input type="hidden" name="AJAX_UPLOAD" value="1">');
        }
        // 创建 modal window
        if (this.modal_type == 'bootstrap') {
            var m = $('#'+this.id_modal),
                w = action.width || self.modal_width
                t = action.top || 20;

            if (m.length > 0) m.remove();
            var mhtml = '<div id="'+this.id_modal+'" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="'+this.id_modal+'Label" aria-hidden="true" data-backdrop="static">'+
                '<div class="modal-header">'+
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>'+
                    '<h3 id="'+this.id_modal+'Label"></h3>'+
                '</div>'+
                '<div class="modal-body" id="'+this.id_modal+'Body"></div>'+
                // '<div class="modal-footer" id="'+this.id_modal+'Footer">'+
                //     '<input type="button" class="btn" onclick="iAction.closeModal()" value="关闭"/>'+
                // '</div>'+
            '</div>';
            m = $(mhtml);
            m.appendTo(document.body);
            if (w) $('#'+this.id_modal).css('width', w+'px');
            t += $(document).scrollTop();
            $('#'+this.id_modal).css('top', t+'px');
			$('#'+this.id_modal+'Body').css({'max-height': 'none !important'});			
			$('#'+this.id_modal+'Label').html(action.title);
			$('#'+this.id_modal+'Body').html(html);
			/*var form_actions = $('.form-actions').html();
			if (form_actions) {
			    $('.form-actions', m).remove();
			    $('#'+this.id_modal+'Footer').html(form_actions);
			}*/
			m.on('hidden', function(){
			    m.remove();
			});
            /**
             * Month select in datepicker inside a bootstrap-modal won't work!
             * @link http://stackoverflow.com/questions/22050641/month-select-in-datepicker-inside-a-bootstrap-modal-wont-work-in-firefox
             */
            m.on('show', function(){
                $.fn.modal.Constructor.prototype.enforceFocus = function () { };
            });
			m.modal('show');
            /*if (typeof action.movable == 'undefined' || action.movable) {
                m.draggable({ handle: '#' + self.id_modal + ' .modal-header' });
                $('.editable, .selectable, select, input, textarea', m).mousedown(function (e) {
                    m.draggable('disable');
                }).mouseup(function (e) {
                    m.draggable('enable');
                });
            }*/
        } else {
            var $dialog = ($('#dialog').length ? $('#dialog') : $('<div id="dialog"></div>'))
                .html(html)
                .dialog({
                    dialogClass: 'modal-win',
                    modal: true,
                    title: action.title,
                    'width': action.width || '655',
                    position: ['center', action.top || 50]
                });
            // 隐藏modal窗口里的modal-header
            $('.module-header', '#dialog').hide();
        }
        // 执行 HTML 里边包含的 JavaScript 代码
        $eval(js);
    },
    
    /**
     * 关闭 modal 窗口。
     */
    closeModal: function(){
        if (this.modal_type == 'bootstrap') {
            $('#'+this.id_modal).modal('hide');
        } else {
            $('#dialog').dialog('close'); 
        }
    }
    
}
